/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.txweb.support;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2009-8-18
 * Time: 20:46:21
 *  提供hessian远程调研接口
 * com.jspx.txweb.support.ServiceAction
 *
 这里是分布式接口，而不是对外到开放接口,分布式的权限很高，不判断逻辑权限，直接操作数据DAO
 客户端调用用户例子
 String url = "http://127.0.0.1:8080/user/service/memberDAO.jhtml?namespace=user&key=A129837092187492381749083279174B";
 HessianProxyFactory factory = new HessianProxyFactory();
 JUserMemberDAO memberDAO =(JUserMemberDAO)factory.create(url);
 List<Member> members = memberDAO.getList(null,null,null,null,null,1,10,false);
 for (Member member:members)
 {
    System.out.println("---------------out=" + member.toString());
 }

 服务器配置
 TXWeb配置
 <package extends="user" namespace="user/service">
    <action name="*" caption="远程接口" class="serviceAction" pass="defInterceptor;licenseInterceptor" />
 </package>
 Sioc 配置

 <sioc namespace="user/service" extends="user">
 <bean id="serviceAction" class="com.jspx.txweb.support.ServiceAction" singleton="false">
    <string name="safeAllow">*</string>
 </bean>
 <bean id="memberService" class="jspx.user.service.impl.MemberServiceImpl" singleton="false"/>
 </sioc>

 远程调研接口
 <sioc namespace="user/service" extends="user">
 <bean id="serviceAction" class="com.jspx.txweb.support.ServiceAction" singleton="false">
 <string name="verifyKey">A129837092187492381749083279174B</string>
 <string name="ips">127.0.0.1;192.168.0.*</string>
 <array class="string" name="allowName">
 <value>memberDAO</value>
 <value>memberService</value>
 </array>
 </bean>
 <bean id="memberService" class="jspx.user.service.impl.MemberServiceImpl" singleton="false"/>
 </sioc>
 */

import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianSkeleton;
import com.caucho.services.server.GenericService;
import com.caucho.services.server.ServiceContext;
import com.jspx.boot.EnvFactory;
import com.jspx.boot.environment.Environment;
import com.jspx.sioc.BeanFactory;
import com.jspx.txweb.annotation.HttpMethod;
import com.jspx.txweb.annotation.Safety;
import com.jspx.txweb.env.TXWeb;
import com.jspx.txweb.util.RequestUtil;
import com.jspx.txweb.util.TXWebUtil;
import com.jspx.utils.*;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;

/**
 * 注意非特殊情况，不要拦截        extends HessianServlet
 */
@HttpMethod(caption = "远程外部调用接口")
public class ServiceAction extends ActionSupport {
    private BeanFactory beanFactory = EnvFactory.getBeanFactory();
    public ServiceAction() {

    }

    private SerializerFactory serializerFactory;
    private Class findRemoteAPI(Class implClass) {
        if (implClass == null || implClass.equals(GenericService.class)) return null;
        Class[] interfaces = implClass.getInterfaces();
        if (interfaces.length == 1)  return interfaces[0];
        return findRemoteAPI(implClass.getSuperclass());
    }

    private String namespace = null;
    public String getNamespace() {
        return namespace;
    }

    /**
     * 允许调用的IOC命名空间,只能一个,多个可以配置本一个命名空间里边,一般一个应用只要一个接口就好
     *
     * @param namespace
     */
    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    private String verifyKey = Environment.defaultDrug;
    @Safety(request = false)
    public void setVerifyKey(String verifyKey) {
        this.verifyKey = verifyKey;
    }

    private String ips = "*";
    @Safety(request = false)
    public void setIps(String ips) {
        this.ips = ips;
    }

    private String[] allowName = null;
    @Safety(request = true)
    public void setAllowName(String[] allowName) {
        this.allowName = allowName;
    }

    private String key = "";
    @Safety(request = true,length = 100)
    public void setKey(String key) {
        this.key = key;
    }

    public SerializerFactory getSerializerFactory() {
        if (serializerFactory == null)
        {
            serializerFactory = new SerializerFactory();
            serializerFactory.setSendCollectionType(true);
        }
        return serializerFactory;
    }

    public void setSerializerFactory(SerializerFactory serializerFactory) {
        this.serializerFactory = serializerFactory;
    }



    public String execute() throws Exception {
        //安全判断 ,关闭远程端口，所有的都不能使用
        if (!request.getMethod().equals("POST")) {
            TXWebUtil.errorPrint("Service",response,531);
            return NONE;
        }

        if (!IpUtil.interiorly(ips,RequestUtil.getRemoteAddr(request)))
        {
            TXWebUtil.errorPrint("Service ip is not allow",response,532);
            return NONE;
        }

        if (verifyKey!=null&&!verifyKey.equals(key))
        {
            TXWebUtil.errorPrint("Service key is error",response,533);
            return NONE;
        }

        //action名称作为bean 名称
        String beanName = getEnv(Key_ActionName);
        if (StringUtil.isNULL(beanName)|| !StringUtil.isGoodName(beanName,1))
        {
            TXWebUtil.errorPrint("Service name is bad",response,534);
            return NONE;
        }

        if (!ArrayUtil.inArray(allowName,beanName, true))
        {
            TXWebUtil.errorPrint("Service name not safe",response,535);
            return NONE;
        }

        //如果命名空间为空，得到当前的命名空间
        if (StringUtil.isNULL(namespace)) {
            namespace = getEnv(Key_Namespace);
        }

        //确保调用到命名空间总配置到命名空间范围内
        if (!namespace.startsWith(getRootNamespace()))
        {
            TXWebUtil.errorPrint("Service namespace not safe",response,536);
            return NONE;
        }
        Object bean = beanFactory.getBean(beanName,namespace);
        if (bean==null)
        {
            TXWebUtil.errorPrint("Service name is not found",response,537);
            return NONE;
        }
        if (ClassUtil.isSetMethod(bean.getClass(), "setRequest")) {
             BeanUtil.setSimpleProperty(bean, "setRequest", request);
        }
        if (ClassUtil.isSetMethod(bean.getClass(), "setRequest")) {
            BeanUtil.setSimpleProperty(bean, "setResponse", response);
        }
        Class implClass = findRemoteAPI(bean.getClass());
        String serviceId = bean.getClass().getName();
        String objectId = request.getParameter("id");
        if (objectId == null)
          objectId = request.getParameter("ejbid");
        HessianSkeleton objectSkeleton = new HessianSkeleton(bean, implClass);
        objectSkeleton.setDebug(true);
        try {
            ServiceContext.begin(request, response, serviceId, objectId);
            response.setContentType("x-application/hessian");
            InputStream is = request.getInputStream();
            OutputStream os = response.getOutputStream();
            objectSkeleton.invoke(is, os, getSerializerFactory());
        }
        finally {
            ServiceContext.end();
        }
        return NONE;
    }
}